package cn.yx.framework.configuration;


import cn.yx.framework.cache.RedisJsonSerializer;
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import javax.sql.DataSource;
import java.nio.charset.Charset;
import java.sql.SQLException;

/**
 * 数据源配置<br>
 * 共分为三块内容：
 * 1.JDBC数据源配置
 * 2.Redis数据源配置
 * 3.SpringData(ORM)配置
 *
 * @author chenw
 * @create 2016-12-29 11:32
 * @email javacspring@gmail.com
 */
@Configuration
@MapperScan(basePackages = "cn.com.yx.ms.persistence")
public class DataSourceConfig {

    @Bean
    public RedisConfig redisConfig() {
        return new RedisConfig();
    }

    @Bean
    public JdbcConfig jdbcConfig() {
        return new JdbcConfig();
    }

    @Bean
    public HibernateConfig hibernateConfig() {
        return new HibernateConfig();
    }

    @Bean
    public StringRedisSerializer stringRedisSerializer() {
        return new StringRedisSerializer(Charset.forName("UTF-8"));
    }

    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(redisConfig().getMaxIdle());
        jedisPoolConfig.setMaxWaitMillis(redisConfig().getMaxWaitMillis());
        jedisPoolConfig.setMaxTotal(redisConfig().getMaxTotal());
        jedisPoolConfig.setTestOnBorrow(redisConfig().isTestOnBorrow());
        return jedisPoolConfig;
    }

    @Bean(name = "jedisConnectionFactory")
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName(redisConfig().getHost());
        jedisConnectionFactory.setPort(redisConfig().getPort());
        jedisConnectionFactory.setPassword(redisConfig().getPassword());
        jedisConnectionFactory.setUsePool(redisConfig().isUsePool());
        jedisConnectionFactory.setPoolConfig(jedisPoolConfig());
        jedisConnectionFactory.setDatabase(redisConfig().getDatabase());
        return jedisConnectionFactory;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {

        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        redisTemplate.setKeySerializer(stringRedisSerializer());
        redisTemplate.setStringSerializer(stringRedisSerializer());

        RedisJsonSerializer redisJsonSerializer = new RedisJsonSerializer();
        redisTemplate.setValueSerializer(redisJsonSerializer);

        redisTemplate.setHashKeySerializer(stringRedisSerializer());
        redisTemplate.setHashValueSerializer(redisJsonSerializer);
        return redisTemplate;
    }

    @Bean(name = "dataSource", destroyMethod = "close")
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();

        JdbcConfig jdbcConfig = jdbcConfig();

        dataSource.setDriverClassName(jdbcConfig.getDriver());
        dataSource.setUrl(jdbcConfig.getUrl());
        dataSource.setUsername(jdbcConfig.getUserName());
        dataSource.setPassword(jdbcConfig.getPassword());

        dataSource.setInitialSize(jdbcConfig.getInitialSize());
        dataSource.setMinIdle(jdbcConfig.getMinIdle());
        dataSource.setMaxActive(jdbcConfig.getMaxActive());
        dataSource.setMaxWait(jdbcConfig.getMaxWait());

        //若使用Oracle，则把poolPreparedStatements配置为true，mysql可以配置为false。分库分表较多的数据库，建议配置为false。
        if (jdbcConfig.getDriver().equalsIgnoreCase("oracle.jdbc.driver.OracleDriver")) {
            dataSource.setPoolPreparedStatements(true);
        } else {
            dataSource.setPoolPreparedStatements(false);
        }
        //SQL执行时间统计(拦截检测时间不含在SQL执行时间内，反之将wall,stat顺序对调)
        try {
            dataSource.setFilters("wall,stat");
        } catch (SQLException e) {
            throw new RuntimeException("Configuration Wallfilter failure", e);
        }
        return dataSource;
    }

    @ConfigurationProperties(prefix = "datasource.jdbc")
    static class JdbcConfig {

        private String url;
        private String userName;
        private String password;
        private String driver;
        private int initialSize;
        private int minIdle;
        private int maxActive;
        private int maxWait;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getDriver() {
            return driver;
        }

        public void setDriver(String driver) {
            this.driver = driver;
        }

        public int getInitialSize() {
            return initialSize;
        }

        public void setInitialSize(int initialSize) {
            this.initialSize = initialSize;
        }

        public int getMinIdle() {
            return minIdle;
        }

        public void setMinIdle(int minIdle) {
            this.minIdle = minIdle;
        }

        public int getMaxActive() {
            return maxActive;
        }

        public void setMaxActive(int maxActive) {
            this.maxActive = maxActive;
        }

        public int getMaxWait() {
            return maxWait;
        }

        public void setMaxWait(int maxWait) {
            this.maxWait = maxWait;
        }
    }

    @ConfigurationProperties(prefix = "datasource.redis")
    static class RedisConfig {

        private String host;
        private int port;
        private String password;
        private boolean usePool;
        private int maxIdle;
        private int maxWaitMillis;
        private int maxTotal;
        private boolean testOnBorrow;
        private int database;

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public boolean isUsePool() {
            return usePool;
        }

        public void setUsePool(boolean usePool) {
            this.usePool = usePool;
        }

        public int getMaxIdle() {
            return maxIdle;
        }

        public void setMaxIdle(int maxIdle) {
            this.maxIdle = maxIdle;
        }

        public int getMaxWaitMillis() {
            return maxWaitMillis;
        }

        public void setMaxWaitMillis(int maxWaitMillis) {
            this.maxWaitMillis = maxWaitMillis;
        }

        public int getMaxTotal() {
            return maxTotal;
        }

        public void setMaxTotal(int maxTotal) {
            this.maxTotal = maxTotal;
        }

        public boolean isTestOnBorrow() {
            return testOnBorrow;
        }

        public void setTestOnBorrow(boolean testOnBorrow) {
            this.testOnBorrow = testOnBorrow;
        }

        public int getDatabase() {
            return database;
        }

        public void setDatabase(int database) {
            this.database = database;
        }
    }

    @ConfigurationProperties(prefix = "datasource.hibernate")
    static class HibernateConfig {

        private String dialect;
        private boolean generateDdl;
        private int maxFetchDepth;
        private int fetchSize;
        private int batchSize;
        private boolean hbm2ddlAuto;
        private boolean showSql;
        private boolean formatSql;
        private String validationMode;
        private boolean statistics;
        private boolean userQueryCache;
        private boolean useSecondCache;

        public String getDialect() {
            return dialect;
        }

        public void setDialect(String dialect) {
            this.dialect = dialect;
        }

        public boolean isGenerateDdl() {
            return generateDdl;
        }

        public void setGenerateDdl(boolean generateDdl) {
            this.generateDdl = generateDdl;
        }

        public int getMaxFetchDepth() {
            return maxFetchDepth;
        }

        public void setMaxFetchDepth(int maxFetchDepth) {
            this.maxFetchDepth = maxFetchDepth;
        }

        public int getFetchSize() {
            return fetchSize;
        }

        public void setFetchSize(int fetchSize) {
            this.fetchSize = fetchSize;
        }

        public int getBatchSize() {
            return batchSize;
        }

        public void setBatchSize(int batchSize) {
            this.batchSize = batchSize;
        }

        public boolean isHbm2ddlAuto() {
            return hbm2ddlAuto;
        }

        public void setHbm2ddlAuto(boolean hbm2ddlAuto) {
            this.hbm2ddlAuto = hbm2ddlAuto;
        }

        public boolean isShowSql() {
            return showSql;
        }

        public void setShowSql(boolean showSql) {
            this.showSql = showSql;
        }

        public boolean isFormatSql() {
            return formatSql;
        }

        public void setFormatSql(boolean formatSql) {
            this.formatSql = formatSql;
        }

        public String getValidationMode() {
            return validationMode;
        }

        public void setValidationMode(String validationMode) {
            this.validationMode = validationMode;
        }

        public boolean isStatistics() {
            return statistics;
        }

        public void setStatistics(boolean statistics) {
            this.statistics = statistics;
        }

        public boolean isUserQueryCache() {
            return userQueryCache;
        }

        public void setUserQueryCache(boolean userQueryCache) {
            this.userQueryCache = userQueryCache;
        }

        public boolean isUseSecondCache() {
            return useSecondCache;
        }

        public void setUseSecondCache(boolean useSecondCache) {
            this.useSecondCache = useSecondCache;
        }
    }
}

